package com.ycg.blog.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import com.algolia.search.SearchClient;
import com.algolia.search.SearchIndex;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ycg.blog.common.PageView;
import com.ycg.blog.common.R;
import com.ycg.blog.constants.CacheConstant;
import com.ycg.blog.pojo.*;
import com.ycg.blog.pojo.dto.ArticleDto;
import com.ycg.blog.pojo.dto.PageCondition;
import com.ycg.blog.pojo.vo.ArticleInfoVo;
import com.ycg.blog.pojo.vo.ArticleVo;
import com.ycg.blog.pojo.vo.ContentArticleVo;
import com.ycg.blog.pojo.vo.StateVo;
import com.ycg.blog.service.ArticleConfigService;
import com.ycg.blog.service.ArticleService;
import com.ycg.blog.mapper.ArticleMapper;
import com.ycg.blog.service.ArticleTagService;
import com.ycg.blog.service.TagService;
import com.ycg.blog.utils.CacheUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * @author lenovo
 * @description 针对表【blog_article(文章信息)】的数据库操作Service实现
 * @createDate 2024-03-02 12:05:04
 */
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article>
        implements ArticleService {

    @Resource
    private ArticleConfigService articleConfigService;

    @Resource
    private TagService tagService;

    @Resource
    private ArticleTagService articleTagService;

    @Resource
    private CacheUtil cacheUtil;

    @Resource
    private SearchClient searchClient;

    @Override
    @Transactional
    public R<String> addOrUpdateArticleInfo(ArticleDto articleDto) {
        int total = Integer.parseInt(cacheUtil.getHash(CacheConstant.SITE_PREVIEW_KEY, "count"));
        if (articleDto.getId() != null) {
            // 说明是修改
            Integer dbCount = getById(articleDto.getId()).getCount();
            int articleCount = articleDto.getContent().length() + articleDto.getSimple().length() - dbCount;
            cacheUtil.setHash(CacheConstant.SITE_PREVIEW_KEY,
                    "count",
                    String.valueOf(dbCount - articleCount + total));
        } else {
            // 新增, 直接加
            cacheUtil.setHash(CacheConstant.SITE_PREVIEW_KEY,
                    "count",
                    String.valueOf(articleDto.getContent().length() + articleDto.getSimple().length() + total));
            // 更新最近更新时间
            cacheUtil.setHash(CacheConstant.SITE_PREVIEW_KEY, "lastUpdate", DateUtil.now());
        }
        // 更新lastUpdate
        cacheUtil.setHash(CacheConstant.SITE_PREVIEW_KEY, "lastUpdate", DateUtil.now());
        // 保存文章信息
        Article article = new Article();
        article.setId(articleDto.getId());
        article.setTitle(articleDto.getTitle());
        article.setSimple(articleDto.getSimple());
        article.setContent(articleDto.getContent());
        article.setCount(articleDto.getContent().length() + articleDto.getSimple().length());
        saveOrUpdate(article);

        // 保存文章配置信息
        articleConfigService.saveOrUpdateByArticleId(article.getId(), articleDto.getCategory().getId());

        // 新增标签信息, 只需新增不重复的标签
        List<Tag> tags = articleDto.getTags();

        tagService.saveByName(tags);

        // 删除之前的标签和文章关联信息
        articleTagService.removeByArticleId(article.getId());

        // 新增标签和文章关联信息
        for (Tag tag : tags) {
            ArticleTag articleTag = new ArticleTag();
            articleTag.setArticleId(article.getId());
            articleTag.setTagId(tag.getId());
            LambdaUpdateWrapper<ArticleTag> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(ArticleTag::getArticleId, article.getId())
                    .eq(ArticleTag::getTagId, tag.getId());
            articleTagService.saveOrUpdate(articleTag, wrapper);
        }
        // 保存信息到algolia
        SearchIndex<Article> index = searchClient.initIndex("article", Article.class);
        article.setObjectID(String.valueOf(article.getId()));
        index.saveObject(article);
        // 清理缓存
        clearCache();
        return R.success("新增成功");
    }

    /**
     * 添加文章后清理缓存
     */
    private void clearCache() {
        cacheUtil.removeByPattern("blog:*");
    }


    @Override
    public R<PageView<ArticleVo>> getArticleInfoList(PageCondition pageCondition) {
        boolean isSimple = isSimpleList(pageCondition);
        if (!isSimple) {
            // 是条件查询, 需要登录认证
            if (!StpUtil.isLogin()) {
                return R.error("请先登录");
            }
        }
        String key = CacheConstant.ARCHIVES_KEY + pageCondition.getCurrent();
        if (isSimple && cacheUtil.hasKey(key)) {
            PageView<ArticleVo> pageView = cacheUtil.get(key, new TypeReference<>() {
            });
            return R.success(pageView);
        }

        // 模糊查询 如果有值则在两边添加 %
        if (StrUtil.isNotEmpty(pageCondition.getKeyword())) {
            pageCondition.setKeyword("%" + pageCondition.getKeyword() + "%");
        }
        Page<ArticleVo> page = getBaseMapper().geArticleInfoList(new Page<>(pageCondition.getCurrent(), pageCondition.getPageSize()), pageCondition);
        PageView<ArticleVo> pageView = new PageView<>();
        List<ArticleVo> pageList = page.getRecords();
        pageView.setPageList(pageList);
        pageView.setTotal(page.getTotal());

        // 查询标签
        ArticleMapper baseMapper = getBaseMapper();
        for (ArticleVo articleVo : pageList) {
            List<Tag> tags = baseMapper.getTagsById(articleVo.getArticleId());
            articleVo.setTagList(tags);
        }
        if (isSimple) {
            cacheUtil.set(key, pageView);
        }
        return R.success(pageView);
    }

    /**
     * 是否是简单的分页查询, 只缓存简单的分页查询, 条件查询不缓存
     *
     */
    private boolean isSimpleList(PageCondition pageCondition) {
        return StrUtil.isBlank(pageCondition.getCategory())
                && StrUtil.isBlank(pageCondition.getKeyword())
                && StrUtil.isBlank(pageCondition.getDateFrom())
                && StrUtil.isBlank(pageCondition.getDateTo());
    }

    @Override
    public R<ArticleInfoVo> getArticleInfo(Integer articleId) {
        ArticleInfoVo articleInfo = getBaseMapper().getArticleInfo(articleId);
        List<Tag> tags = getBaseMapper().getTagsById(articleId);
        articleInfo.setTags(tags);
        return R.success(articleInfo);
    }

    @Override
    public R<PageView<ContentArticleVo>> getContentArticleByCategoryName(Integer current, String categoryName) {
        String key = CacheConstant.CONTENT_KEY + categoryName + current;
        if (cacheUtil.hasKey(key)) {
            PageView<ContentArticleVo> cachePageView = cacheUtil.get(key, new TypeReference<>() {
            });
            return R.success(cachePageView);
        }
        Page<ContentArticleVo> page = new Page<>(current, 10);
        List<ContentArticleVo> homeArticles = getBaseMapper().getHomeArticleByCategoryName(categoryName, page);
        PageView<ContentArticleVo> pageView = new PageView<>();
        pageView.setTotal(page.getTotal());
        pageView.setPageList(homeArticles);
        // 首页缓存久一点1小时, 第二页半小时, 第三页依次减少15分钟, 第四页不缓存
        cacheByCurrent(key, pageView, current);
        return R.success(pageView);
    }

    /**
     * 根据当前页设置缓存时间
     */
    private void cacheByCurrent(String key, PageView<ContentArticleVo> pageView, Integer current) {
        long timeout = 100 - 30L * current;
        cacheUtil.setTTL(key, pageView, timeout > 0 ? timeout : 10, TimeUnit.MINUTES);
    }

    @Override
    @Transactional
    public R<ArticleVo> getDetailArticle(Integer articleId) {
        // 更新文章配置信息 增加浏览量
        articleConfigService.addView(articleId);
        String aboutKey = CacheConstant.ABOUT_KEY + articleId;
        String detailKey = CacheConstant.DETAIL_KEY + articleId;
        if (cacheUtil.hasKey(aboutKey)) {
            // 返回关于我页面 同时新增浏览量
            ArticleVo vo = cacheUtil.get(aboutKey, ArticleVo.class);
            vo.setViews(String.valueOf(Integer.parseInt(vo.getViews()) + 1));
            cacheUtil.set(aboutKey, vo);
            return R.success(vo);
        } else if (cacheUtil.hasKey(detailKey)) {
            // 返回普通页面 同时新增浏览量
            ArticleVo vo = cacheUtil.get(detailKey, ArticleVo.class);
            vo.setViews(String.valueOf(Integer.parseInt(vo.getViews()) + 1));
            cacheUtil.set(detailKey, vo);
            return R.success(vo);
        }
        ArticleVo vo = getBaseMapper().getDetailArticle(articleId);
        // 关于我永久缓存
        if (Objects.equals(vo.getCategoryName(), "关于")) {
            // 缓存关于我页面
            cacheUtil.set(aboutKey, vo);
        } else {
            // 缓存普通页面
            cacheUtil.setTTL(detailKey, vo, 30, TimeUnit.MINUTES);
        }
        return R.success(vo);
    }

    @Override
    public R<StateVo> getState() {
        String key = CacheConstant.SITE_STATE_KEY;
        StateVo vo = cacheUtil.get(key, StateVo.class);
        if (vo == null) {
            vo = getBaseMapper().getState();
            cacheUtil.set(key, vo);
        }
        return R.success(vo);
    }

    /**
     * 获取关于我分类下的文章id
     */
    @Override
    public R<Integer> getAboutMeId() {
        Long id = getBaseMapper().getAboutMeId();
        return R.success(id.intValue());
    }
}




